#pragma once

#define forceinline __forceinline

namespace krryn{
namespace system_imp{
	typedef int fp_type; // fp_type is assumed to be 32 bit
	typedef long long fp_next_type; // fp_next_type is assumed to be 64 bit

	template<typename _Type>
	struct fp_values{
		forceinline static _Type one(){ return 1; }
		forceinline static _Type zero(){ return 0; }
		forceinline static _Type half(){ return 0.5; }
	};

	template<typename _Type>
	forceinline _Type cast_to(float a_Value){
		return (_Type)a_Value;
	}

	template<typename _Type>
	forceinline _Type fraction(_Type a_Value){
		int l_Temp = (int)a_Value;
		return a_Value - (_Type)l_Temp;
	}

	template<typename _Type>
	forceinline int fp_ceil(_Type a_Value){
		return (int)ceil(a_Value);
	}

	template<typename _Type>
	forceinline int to_int(_Type a_Value){
		return (int)a_Value;
	}

	template<typename _Type>
	forceinline _Type mul_int(const _Type a_Right, const int a_Left){
		return a_Right * (_Type)a_Left;
	}

	template<typename _Type>
	forceinline _Type sub_int(const int a_Right, const _Type a_Left){
		return a_Right - a_Left;
	}

	template<typename _Type>
	forceinline _Type mul(const _Type a_Right, const _Type a_Left){
		return a_Left * a_Right;
	}

	template<typename _Type>
	forceinline _Type div(const _Type a_Left, const _Type a_Right){
		return a_Left / a_Right;
	}

	template<typename _Type>
	forceinline _Type inverse(_Type a_Value){
		return ((_Type)1) / a_Value;
	}

	template<typename _Type>
	forceinline int __fastcall log2(_Type a_A){
		return (int)log(a_A, 2);
	}

	template<typename _Type>
	forceinline _Type det_2x2(_Type a_A, _Type a_B, _Type a_C, _Type a_D){
		return a_A * a_D - a_B * a_C;
	}

	template<>
	int __fastcall log2<float>(float a_Value){
		int log = *(int*)&a_Value;
		log >>= 23;
		log -= 127;
		return log;
	}

	/// integer (12:20 fixed point) implementation starts here

	// 18 <= SHIFT_FACTOR <= 21 seems to work OK
	#define SHIFT_FACTOR 20

	template<>
	struct fp_values<fp_type>{
		forceinline static int one(){ return 1 << SHIFT_FACTOR; }
		forceinline static int zero(){ return 0 << SHIFT_FACTOR; }
		forceinline static int half(){ return 1 << (SHIFT_FACTOR - 1); }
	};

	template<>
	forceinline fp_type cast_to<fp_type>(float a_Value){
		return (fp_type)(a_Value * (1 << SHIFT_FACTOR));
	}

	template<>
	forceinline int __fastcall log2<fp_type>(fp_type a_Value){
		__asm sar ecx, SHIFT_FACTOR;
		__asm bsr ecx, ecx;
		__asm mov eax, ecx;
	}

	template<>
	forceinline fp_type det_2x2<fp_type>(fp_type a_A, fp_type a_B, fp_type a_C, fp_type a_D){
		fp_next_type l_Values[] = {
			(fp_next_type)a_A * (fp_next_type)a_D,
            (fp_next_type)a_B * (fp_next_type)a_C
		};
		
		return (fp_type)((l_Values[0] - l_Values[1])  >> SHIFT_FACTOR);
    }

	template<>
	forceinline fp_type fraction<fp_type>(fp_type a_Value){
		return (a_Value & ((1 << SHIFT_FACTOR) - 1));
	}

	template<>
	forceinline int to_int(fp_type a_Value){
		return (int)(a_Value >> SHIFT_FACTOR);
	}

	template<>
	forceinline int fp_ceil(fp_type a_Value){
		a_Value += ((1 << SHIFT_FACTOR) - 1);
		return a_Value >> SHIFT_FACTOR;
	}

	template<>
	forceinline fp_type sub_int(const int a_Right, const fp_type a_Left){
		return (a_Right << SHIFT_FACTOR) - a_Left;
	}

	template<>
	forceinline fp_type mul<fp_type>(const fp_type a_Left, const fp_type a_Right){
		return (fp_type)(((fp_next_type)a_Left * a_Right) >> SHIFT_FACTOR);
	}

	template<>
	forceinline fp_type mul_int<fp_type>(const fp_type a_Left, const int a_Right){
		return mul<fp_type>(a_Left, a_Right << SHIFT_FACTOR);
	}

	template<>
	forceinline fp_type div<fp_type>(const fp_type a_Left, const fp_type a_Right){
		return (fp_type)((((fp_next_type)a_Left) << SHIFT_FACTOR) / a_Right);
	}

	template<>
	forceinline fp_type inverse<fp_type>(fp_type a_Value){
		return div<fp_type>(fp_values<fp_type>::one(), a_Value);
	}
}
}
